﻿using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;

using UMC.Net;

namespace UMC.Host
{

    public class HttpMimeSocket : HttpMime
    {
        public override string Scheme => "http";

        public HttpMimeSocket(Socket socket, String ip)
        {
            this._socket = socket;
            this.ActiveTime = UMC.Data.Utility.TimeSpan();

            this.pid = socket.GetHashCode();

            this._Host = "127.0.0.1";

            this._remoteIpAddress = ip;

            HttpMimeServier.httpMimes.TryAdd(pid, this);
            SocketAsyncEventArgs eventArgs = new SocketAsyncEventArgs();

            eventArgs.SetBuffer(_data);
            eventArgs.Completed += Http;
            if (!this._socket.ReceiveAsync(eventArgs))
            {
                ProcessReceive(eventArgs);
            }
        }
        Socket _socket;
        int pid = 0;
        public override int Id => pid;

        String _remoteIpAddress, _Host;
        public override String Host => _Host;
        public override String RemoteIpAddress => _remoteIpAddress;

        public override void Write(byte[] buffer, int offset, int count)
        {
            if (isDispose == false)
            {
                try
                {

                    _socket.Send(buffer, offset, count, SocketFlags.None);
                }
                catch
                {
                    this.Dispose();
                }
            }
        }
        UMC.Net.MimeRequest request;
        private void Http(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
            }
        }
        bool IsHeader = true;
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                this.ActiveTime = UMC.Data.Utility.TimeSpan();
                try
                {
                    if (request == null)
                    {
                        if (IsHeader)
                        {
                            IsHeader = true;
                            if (this._data[0] == 0x1)
                            {
                                try
                                {
                                    Subscribe(this._data, 1, e.BytesTransferred - 1);
                                    // _socket = null;
                                }
                                catch //(Exception ex)
                                {

                                }
                                finally
                                {
                                    this.Dispose();
                                }
                                return;
                            }
                        }
                        request = new HttpMimeRequest(this);
                    }
                    request.Receive(this._data, e.Offset, e.BytesTransferred);

                    if (this.isDispose == false && !_socket.ReceiveAsync(e))
                    {
                        ProcessReceive(e);
                    }
                }
                catch (Exception ex)
                {
                    this.OutText(500, ex.ToString());

                    return;
                }
            }
            else
            {
                this.Dispose();

            }
        }



        public override void OutputFinish()
        {

            request = null;
            this.ActiveTime = UMC.Data.Utility.TimeSpan();
            this.TimeOut = 20;

        }
        void Subscribe(byte[] header, int offset, int size)
        {
            var headers = new NameValueCollection();
            var ps = System.Text.Encoding.UTF8.GetString(header, offset, size).Split("\r\n");
            foreach (var s in ps)
            {
                var vi = s.IndexOf(':');
                var key = s.Substring(0, vi);
                var value = s.Substring(vi + 2);
                headers.Add(key, value);

            }

            int length = 0;
            var _subscribe = UMC.Net.NetSubscribe.Subscribe(headers, this._remoteIpAddress, HttpMimeServier.Server, _socket, UMC.Data.WebResource.Instance().Provider["appSecret"]);
            if (_subscribe != null)
            {

                length += $"HTTP/1.1 101 {HttpStatusDescription.Get(101)}\r\n".WriteBytes(header, length);
                length += "Connection: upgrade\r\n".WriteBytes(header, length);
                length += "Upgrade: websocket\r\n".WriteBytes(header, length);
                length += $"UMC-Publisher-Key: {HttpMimeServier.Server}\r\n".WriteBytes(header, length);
                length += "Server: Apiumc\r\n\r\n".WriteBytes(header, length);
                _socket.Send(header, 0, length, SocketFlags.None);
                _subscribe.Publish();
                _socket = null;
            }
            else
            {
                var text = "数据订阅连接验证不通过";
                length += $"HTTP/1.1 401 {HttpStatusDescription.Get(401)}\r\n".WriteBytes(header, length);
                length += $"Content-Type: text/plain; charset=utf-8\r\n".WriteBytes(header, length);
                length += $"Content-Length: {System.Text.Encoding.UTF8.GetByteCount(text)}\r\n".WriteBytes(header, length);
                length += "Connection: close\r\n".WriteBytes(header, length);
                length += "Server: Apiumc\r\n\r\n".WriteBytes(header, length);
                length += text.WriteBytes(header, length);

                _socket.Send(header, 0, length, SocketFlags.None);

            }
        }
        protected override void WebSocket(NetContext context)
        {
            if (context.Tag is NetHttpRequest)
            {
                var webr = context.Tag as NetHttpRequest;
                this.WebSocket(webr);
            }
        }
        bool isDispose = false;
        public override void Dispose()
        {
            if (isDispose == false)
            {
                if (this._data != null)
                {
                    System.Buffers.ArrayPool<byte>.Shared.Return(this._data);
                    _data = null;
                }
                isDispose = true;
                try
                {
                    _socket?.Shutdown(SocketShutdown.Both);
                    _webSocket?.Dispose();
                }
                catch
                {

                }
                _socket?.Close();
            }
            HttpMimeServier.httpMimes.TryRemove(pid, out var _);

        }
        async void WebSocket(NetHttpRequest webRequest)
        {
            try
            {
                var url = webRequest.Address;


                webRequest.Headers["Connection"] = "Upgrade";


                var client = new Socket(SocketType.Stream, ProtocolType.Tcp);

                await client.ConnectAsync(await NetProxy.DNS(url.Host), url.Port);


                if (url.Scheme == "https")
                {
                    SslStream ssl = new SslStream(new NetworkStream(client, true), false, (sender, certificate, chain, sslPolicyErrors) => true);
                    await ssl.AuthenticateAsClientAsync(url.Host, null, SslProtocols.None, false);

                    await ssl.WriteAsync(_data, 0, UMC.Net.NetHttpResponse.Header(webRequest, _data));
                    int size = await ssl.ReadAsync(_data, 0, _data.Length);

                    if (NetBridge.ResponseHeader(_data, 0, size, new NameValueCollection(), out var statucode) && statucode == HttpStatusCode.SwitchingProtocols)
                    {
                        request = _webSocket = new HttpsWebSocket(this, ssl);

                        this._socket.Send(_data, 0, size, SocketFlags.None);
                    }
                    else
                    {
                        this._socket.Send(_data, 0, size, SocketFlags.None);
                        this.Dispose();
                    }
                }
                else
                {
                    client.Send(_data, 0, UMC.Net.NetHttpResponse.Header(webRequest, _data), SocketFlags.None);
                    var size = client.Receive(_data, 0, _data.Length, SocketFlags.None);
                    if (NetBridge.ResponseHeader(_data, 0, size, new NameValueCollection(), out var statucode) && statucode == HttpStatusCode.SwitchingProtocols)
                    {
                        request = _webSocket = new HttpWebSocket(this, client);
                        this._socket.Send(_data, 0, size, SocketFlags.None);
                    }
                    else
                    {

                        this._socket.Send(_data, 0, size, SocketFlags.None);
                        this.Dispose();
                    }

                }

                HttpMimeServier.httpMimes.TryRemove(this.pid, out var _);
            }
            catch (Exception ex)
            {
                OutText(500, ex.ToString());
            }
        }
        WebSocket _webSocket;
        byte[] _data = System.Buffers.ArrayPool<byte>.Shared.Rent(0x600);
    }
}

